#     D I S T C H E C K _ R E P O _ V E R I F Y . C M A K E . I N
#
# BRL-CAD
#
# Copyright (c) 2011-2025 United States Government as represented by
# the U.S. Army Research Laboratory.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
#
# 3. The name of the author may not be used to endorse or promote
# products derived from this software without specific prior written
# permission.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
###
if(EXISTS "@CMAKE_SOURCE_DIR@/src/testing")
  message(
    FATAL_ERROR
    "\nThe directory @CMAKE_SOURCE_DIR@/src/testing is present.  Remove this directory from the source tree before proceeding with distcheck."
  )
endif(EXISTS "@CMAKE_SOURCE_DIR@/src/testing")

set(NOT_DISTCHECK_READY 0)
set(CMAKE_BINARY_DIR "@CMAKE_BINARY_DIR@")

# Avoid empty items in lists, so we don't trigger a CMake warning
function(scrub_list SLIST)
  set(NONEMPTY)
  foreach(sf ${${SLIST}})
    if(NOT "${sf}" STREQUAL "")
      list(APPEND NONEMPTY "${sf}")
    endif(NOT "${sf}" STREQUAL "")
  endforeach(sf ${${SLIST}})
  string(REGEX REPLACE "^;" "" NONEMPTY "${NONEMPTY}")
  set(${SLIST} ${NONEMPTY} PARENT_SCOPE)
endfunction(scrub_list SLIST)

# If we're using Git, check what it thinks the contents should be
if(EXISTS "@CMAKE_SOURCE_DIR@/.git")
  # Look for Git - if we don't have the command, we can't query the
  # repository even if the repo information is present.
  find_program(GIT_EXEC git)

  if(GIT_EXEC)
    # We need the Git binary and the source directory must be a
    # checkout of the BRL-CAD subversion repository.  If these two conditions
    # are met, we can collect information from Git

    # By default, assume that nothing has been modified.
    set(GIT_TREE_MODIFIED 0)

    # Get the file list
    unset(GIT_FILES)
    execute_process(
      COMMAND ${GIT_EXEC} ls-tree --full-tree -r --name-only HEAD
      WORKING_DIRECTORY "@CMAKE_SOURCE_DIR@"
      OUTPUT_VARIABLE GIT_STDOUT
      ERROR_VARIABLE GIT_STDERR
      RESULT_VARIABLE RETVAL
    )
    string(REGEX REPLACE "\r?\n" ";" GIT_FILES "${GIT_STDOUT}")
    string(REGEX REPLACE ";$" "" GIT_FILES "${GIT_FILES}")
    if(RETVAL)
      message("")
      message("WARNING: Git does not seem to be working correctly.")
      message("         Output from \"${GIT_EXEC} ls-tree --full-tree -r --name-only HEAD\" in \"@CMAKE_SOURCE_DIR@\":")
      message("${GIT_STDERR}")
    endif(RETVAL)

    # Check whether Git is reporting modified files.
    execute_process(
      COMMAND ${GIT_EXEC} status --porcelain
      WORKING_DIRECTORY "@CMAKE_SOURCE_DIR@"
      OUTPUT_VARIABLE GIT_STDOUT
      ERROR_VARIABLE GIT_STDERR
    )
    set(GIT_STATUS)
    foreach(ITEM ${GIT_STDOUT})
      list(APPEND GIT_STATUS ${ITEM})
    endforeach(ITEM ${GIT_STDOUT})
    # Apparently can get an empty list entry from GIT_STDOUT - remove any empty
    # entries so CMake won't complain.
    list(REMOVE_ITEM "" GIT_STATUS)
    string(REPLACE "\n" ";" GIT_STATUS "${GIT_STATUS}")
    foreach(ITEM ${GIT_STATUS})
      if(${ITEM} MATCHES "^\\?\\?")
        string(REGEX REPLACE "^[?][?][\\t\\ ]*" "" item_path "${ITEM}")
        list(REMOVE_ITEM GIT_FILES ${item_path})
      elseif(${ITEM} MATCHES "^D ")
        # If Git has staged a file for deletion, ignore it unless a
        # file with that name is still present in the tree
        string(REGEX REPLACE "^D[ ][\\t\\ ]*" "" item_path "${ITEM}")
        if(NOT EXISTS "@CMAKE_SOURCE_DIR@/${item_path}")
          list(REMOVE_ITEM GIT_FILES ${item_path})
        else(NOT EXISTS "@CMAKE_SOURCE_DIR@/${item_path}")
          message("\nFile ${item_path} is staged for deletion by Git, but still exists in working tree.")
        endif(NOT EXISTS "@CMAKE_SOURCE_DIR@/${item_path}")
      elseif(${ITEM} MATCHES "^R ")
        string(REGEX REPLACE "^R[ ][\\t\\ ]*" "" item_paths "${ITEM}")
        string(REPLACE " -> " ";" item_path_list "${item_paths}")
        list(GET item_path_list 0 PATH1)
        list(GET item_path_list 1 PATH2)
        list(REMOVE_ITEM GIT_FILES ${PATH1})
        list(APPEND GIT_FILES ${PATH2})
      elseif(${ITEM} MATCHES "^A ")
        string(REGEX REPLACE "^A[ ][\\t\\ ]*" "" item_path "${ITEM}")
        list(APPEND GIT_FILES ${item_path})
      else(${ITEM} MATCHES "^\\?\\?")
        string(REGEX REPLACE "^[ ]M[\\t\\ ]*" "" item_path "${ITEM}")
        set(GIT_TREE_MODIFIED 1)
      endif(${ITEM} MATCHES "^\\?\\?")
    endforeach(ITEM ${GIT_STATUS})

    # Generate a sorted text file with the results of the Git list processing.
    scrub_list(GIT_FILES)
    list(SORT GIT_FILES)
    list(REMOVE_DUPLICATES GIT_FILES)

    # If we've got modified files, we shouldn't use resulting tarballs for a distribution -
    # warn accordingly.
    if(GIT_TREE_MODIFIED)
      message(
        "\n**** NOTE:  Git reports modified source files present in the source tree.\n     Distcheck proceeding, but archives will not be suitable for release.\n     To see the list of modified files, run \"git status --porcelain\"\n"
      )
      set(NOT_DISTCHECK_READY 1)
    endif(GIT_TREE_MODIFIED)

    message(" --- Building file list from Git manifests: Done")
  endif(GIT_EXEC)
endif(EXISTS "@CMAKE_SOURCE_DIR@/.git")

# The build system has kindly created lists of files and directories for us,
# but in order to compare the build system's lists to the others generated
# by this script we need the fully expanded list.  Read and expand...
message(" --- Building list of files known to the build system: ...")
file(STRINGS "@CMAKE_BINARY_DIR@/cmakefiles.cmake" BUILD_FILES)
foreach(BF ${BUILD_FILES})
  get_filename_component(BFA "${BF}" ABSOLUTE BASE_DIR "@CMAKE_SOURCE_DIR@")
  string(REGEX REPLACE "^@CMAKE_SOURCE_DIR@/" "" BFA "${BFA}")
  list(APPEND BFLS ${BFA})
endforeach(BF ${BUILD_FILES})
set(BUILD_FILES "${BFLS}")
list(SORT BUILD_FILES)
list(REMOVE_DUPLICATES BUILD_FILES)
message(" --- Building list of files known to the build system: Done")

# Get the CPack ignore build path value from the parent CMake build
set(CPACK_IGNORE_BUILD_PATH "@CPACK_IGNORE_BUILD_PATH@")

# Determing what files are actually present on the file system at the
# time this is executed (as opposed to what the build system or
# Subversion *expect* to be present) minus files that CPack knows to
# ignore (i.e., things that will not be included in a source archive.)
message(" --- Building list of files actually present in source tree: ...")
file(GLOB_RECURSE FS_FILE_LIST RELATIVE "@CMAKE_SOURCE_DIR@" "@CMAKE_SOURCE_DIR@/*")
# Start scrubbing...
set(FS_FILES)
foreach(pf ${FS_FILE_LIST})
  if(NOT "${pf}" MATCHES "^\\.git/.*")
    if(CPACK_IGNORE_BUILD_PATH)
      if(NOT "${pf}" MATCHES "^${CPACK_IGNORE_BUILD_PATH}.*")
        list(APPEND FS_FILES ${pf})
      endif(NOT "${pf}" MATCHES "^${CPACK_IGNORE_BUILD_PATH}.*")
    else(CPACK_IGNORE_BUILD_PATH)
      list(APPEND FS_FILES ${pf})
    endif(CPACK_IGNORE_BUILD_PATH)
  endif(NOT "${pf}" MATCHES "^\\.git/.*")
endforeach(pf ${FS_FILE_LIST})
set(FS_FILE_LIST ${FS_FILES})
if(NOT FS_FILE_LIST)
  message(FATAL_ERROR "FATAL: No files found in file system search?? ")
endif(NOT FS_FILE_LIST)
# Generate a sorted text file with the results of the CMake build system list processing.
list(REMOVE_DUPLICATES FS_FILE_LIST)
list(SORT FS_FILE_LIST)
message(" --- Building list of files actually present in source tree: Done")

# To generate lists of "interesting" files - things the build system knows about but
# subversion doesn't, files the Subversion system knows about that the build system
# doesn't, and files present in the source tree that *neither* system knows about,
# we need to generate relative compliment sets of the various lists.  Using a technique
# from Eric Noulard on the CMake mailing list:
#
# http://www.cmake.org/pipermail/cmake/2011-September/046214.html
message(" --- Performing comparisons...")

# If we have Git, we can do lots more interesting comparisons.
# Otherwise, just look for files the build system doesn't know about.
if(EXISTS "@CMAKE_SOURCE_DIR@/.git")
  if(GIT_EXEC)
    # Report file in Git but not the build logic:
    set(GIT_FILES_NOT_IN_BUILD ${GIT_FILES})
    list(REMOVE_ITEM GIT_FILES_NOT_IN_BUILD ${BUILD_FILES})
    if(GIT_FILES_NOT_IN_BUILD)
      string(REPLACE ";" "\n" GIT_FILES_NOT_IN_BUILD "${GIT_FILES_NOT_IN_BUILD}")
      message("\nFiles known to Git are not accounted for in build logic: \n${GIT_FILES_NOT_IN_BUILD}\n")
    endif(GIT_FILES_NOT_IN_BUILD)

    # Report file in the build logic but not Git:
    set(BUILD_FILES_NOT_IN_GIT ${BUILD_FILES})
    list(REMOVE_ITEM BUILD_FILES_NOT_IN_GIT ${GIT_FILES})
    if(BUILD_FILES_NOT_IN_GIT)
      string(REPLACE ";" "\n" BUILD_FILES_NOT_IN_GIT "${BUILD_FILES_NOT_IN_GIT}")
      message("\nFiles mentioned in build logic are not checked into the repository: \n${BUILD_FILES_NOT_IN_GIT}\n")
    endif(BUILD_FILES_NOT_IN_GIT)

    # Either of the previous two cases halts distcheck until fixed.
    set(FORCE_DISTCHECK @FORCE_DISTCHECK@)
    if(GIT_FILES_NOT_IN_BUILD OR BUILD_FILES_NOT_IN_GIT)
      if(NOT FORCE_DISTCHECK)
        message(
          FATAL_ERROR
          "ERROR: Distcheck cannot proceed until build files and repo are in sync (set -DFORCE_DISTCHECK=ON to override)"
        )
      endif(NOT FORCE_DISTCHECK)
    endif(GIT_FILES_NOT_IN_BUILD OR BUILD_FILES_NOT_IN_GIT)
  endif(GIT_EXEC)
endif(EXISTS "@CMAKE_SOURCE_DIR@/.git")

set(IGNORED_FS_FILES ${FS_FILE_LIST})
list(REMOVE_ITEM IGNORED_FS_FILES ${BUILD_FILES})
if(IGNORED_FS_FILES)
  string(REPLACE ";" "\n" IGNORED_FS_FILES "${IGNORED_FS_FILES}")
  message(
    "\nFiles unknown to both Version Control System and the Build logic (will not be incorporated into dist):\n${IGNORED_FS_FILES}\n"
  )
endif(IGNORED_FS_FILES)

# Write out the archive status so the build targets can see it later.
if(NOT_DISTCHECK_READY)
  if("${NOT_DISTCHECK_READY}" STREQUAL "1")
    file(
      WRITE
      "@CMAKE_BINARY_DIR@/CMakeTmp/distcheck_remove_archives_if_invalid"
      "execute_process(COMMAND @CMAKE_COMMAND@ -E echo \"Archive files contain modified sources, NOT suitable for distribution.\")\n"
    )
  endif("${NOT_DISTCHECK_READY}" STREQUAL "1")
  file(
    APPEND
    "@CMAKE_BINARY_DIR@/CMakeTmp/distcheck_remove_archives_if_invalid"
    "execute_process(COMMAND @CMAKE_COMMAND@ -E remove \"${CPACK_SOURCE_PACKAGE_FILE_NAME}.tar.gz\")\n"
  )
  file(
    APPEND
    "@CMAKE_BINARY_DIR@/CMakeTmp/distcheck_remove_archives_if_invalid"
    "execute_process(COMMAND @CMAKE_COMMAND@ -E remove \"${CPACK_SOURCE_PACKAGE_FILE_NAME}.tar.bz2\")\n"
  )
  file(
    APPEND
    "@CMAKE_BINARY_DIR@/CMakeTmp/distcheck_remove_archives_if_invalid"
    "execute_process(COMMAND @CMAKE_COMMAND@ -E remove \"${CPACK_SOURCE_PACKAGE_FILE_NAME}.zip\")\n"
  )
else(NOT_DISTCHECK_READY)
  if(EXISTS "@CMAKE_SOURCE_DIR@/.git" AND GIT_EXEC)
    file(WRITE "@CMAKE_BINARY_DIR@/CMakeTmp/distcheck_remove_archives_if_invalid" "")
  endif(EXISTS "@CMAKE_SOURCE_DIR@/.git" AND GIT_EXEC)
  if(EXISTS "@CMAKE_SOURCE_DIR@/.git" AND NOT GIT_EXEC)
    file(
      WRITE
      "@CMAKE_BINARY_DIR@/CMakeTmp/distcheck_remove_archives_if_invalid"
      "execute_process(COMMAND @CMAKE_COMMAND@ -E echo \"Note:  Archive files were generated without being able to compare them to the Git archive master file lists..\")\n"
    )
  endif(EXISTS "@CMAKE_SOURCE_DIR@/.git" AND NOT GIT_EXEC)
  if(NOT EXISTS "@CMAKE_SOURCE_DIR@/.git")
    file(
      WRITE
      "@CMAKE_BINARY_DIR@/CMakeTmp/distcheck_remove_archives_if_invalid"
      "execute_process(COMMAND @CMAKE_COMMAND@ -E echo \"Note:  Archive files were generated without being able to compare them to the Version Control System master file lists.\")\n"
    )
  endif(NOT EXISTS "@CMAKE_SOURCE_DIR@/.git")
endif(NOT_DISTCHECK_READY)

# All done - print result messages and prepare the appropriate final message for the distcheck build.
set(FINAL_DIST_MESSAGE "--- distcheck succeeded ---")
if("${NOT_DISTCHECK_READY}")
  set(FINAL_DIST_MESSAGE "--- distcheck compilation succeeded, archives not ready ---")
endif("${NOT_DISTCHECK_READY}")
if(NOT EXISTS "@CMAKE_SOURCE_DIR@/.git" OR NOT GIT_EXEC)
  set(
    FINAL_DIST_MESSAGE
    "--- distcheck succeeded ---\n*** Archive files pass available checks, but a distcheck using a Version Controlled checkout is recommended for archive files intended for distribution. ***"
  )
endif(NOT EXISTS "@CMAKE_SOURCE_DIR@/.git" OR NOT GIT_EXEC)
file(
  WRITE
  "@CMAKE_BINARY_DIR@/CMakeTmp/distcheck_message"
  "execute_process(COMMAND @CMAKE_COMMAND@ -E echo \"${FINAL_DIST_MESSAGE}\")"
)

# If we got this far, it worked
message("Source Repository Verification: Passed")

# Local Variables:
# tab-width: 8
# mode: cmake
# indent-tabs-mode: t
# End:
# ex: shiftwidth=2 tabstop=8
